#include "stdafx.h"
#include "BigInt.h"

using namespace System;
using namespace System::Text;
using namespace System::Collections::Generic;
using namespace Microsoft::VisualStudio::TestTools::UnitTesting;
using namespace APIAL;

namespace BigIntUnitTest
{
	[TestClass]
	public ref class ComparisonUnitTest
	{
	private:
		TestContext^ testContextInstance;
		static BigInt *op1;
		static BigInt *op2;
		static BigInt *scratch1;
		static BigInt *result1;
		static BigInt *result2;
		static BigInt *expected;

	public: 
		/// <summary>
		///Gets or sets the test context which provides
		///information about and functionality for the current test run.
		///</summary>
		property Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ TestContext
		{
			Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ get()
			{
				return testContextInstance;
			}
			System::Void set(Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ value)
			{
				testContextInstance = value;
			}
		};

		#pragma region Additional test attributes
		//
		//You can use the following additional attributes as you write your tests:
		//
		//Use ClassInitialize to run code before running the first test in the class
		[ClassInitialize()]
		static void MyClassInitialize(Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ testContext) {
			op1 = new BigInt(20, 8);
			op2 = new BigInt(20, 8);
			result1 = new BigInt(20, 8);
			result2 = new BigInt(20, 8);
			scratch1 = new BigInt(20, 8);
			expected = new BigInt(20, 8);
		}

		//Use ClassCleanup to run code after all tests in a class have run
		[ClassCleanup()]
		static void MyClassCleanup() { 
			delete op1;
			delete op2;
			delete result1;
			delete result2;
			delete scratch1;
			delete expected;
		}
		//
		//Use TestInitialize to run code before running each test
		//[TestInitialize()]
		//void MyTestInitialize() {};
		//
		//Use TestCleanup to run code after each test has run
		//[TestCleanup()]
		//void MyTestCleanup() {};
		//
		#pragma endregion 

		[TestMethod]
		void TestLessThanUnnormalized()
		{
			op1->chunks[0] = 0x00;
			op1->chunks[1] = 0x01;
			op1->size = 2;
			op1->positive = true;
			op1->normalized = false;
			
			op2->chunks[0] = 0x81;
			op2->chunks[1] = 0x00;
			op2->size = 2;
			op2->positive = true;
			op2->normalized = false;

			Assert::IsTrue(!(*op2<*op1));
			Assert::IsTrue(op1->normalized);
			Assert::IsTrue(op2->normalized);
		};

		[TestMethod]
		void TestLessThanDifferingSigns() {
			// Equal, but signs are different
			op1->ParseHexadecimalString("FA235CD98", true);
			op2->ParseHexadecimalString("FA235CD98", false);
			Assert::IsTrue(*op2<*op1);			
			op2->positive = true;
			Assert::IsFalse(*op2<*op1);

			// Less than, but signs are different
			op2->ParseHexadecimalString("FA235CD97", true);
			Assert::IsTrue(*op2<*op1);
			op1->positive = false;
			Assert::IsFalse(*op2<*op1);
		}

		[TestMethod]
		void TestGreaterThanUnnormalized()
		{
			op1->chunks[0] = 0x00;
			op1->chunks[1] = 0x01;
			op1->size = 2;
			op1->normalized = false;
			op1->positive = true;

			op2->chunks[0] = 0x81;
			op2->chunks[1] = 0x00;
			op2->size = 2;
			op2->normalized = false;
			op2->positive = true;

			Assert::IsTrue(!(*op1>*op2));
			Assert::IsTrue(op1->normalized);
			Assert::IsTrue(op2->normalized);
		};

		[TestMethod]
		void TestGreaterThanDifferingSigns() {
			// Equal, but signs are different
			op1->ParseHexadecimalString("FA235CD98", true);
			op2->ParseHexadecimalString("FA235CD98", false);
			Assert::IsTrue(*op1>*op2);			
			op2->positive = true;
			Assert::IsFalse(*op1>*op2);

			// Less than, but signs are different
			op2->ParseHexadecimalString("FA235CD97", true);
			Assert::IsTrue(*op1>*op2);
			op1->positive = false;
			Assert::IsFalse(*op1>*op2);
		}

		[TestMethod]
		void TestAllComparisons() {
			op1->ParseHexadecimalString("FA982312CD238723FCD1555", true);
			op2->ParseHexadecimalString("FA982312CD238723FCD1555", true);

			// Operands are equal, differ only in sign
			// Positive, positive
			op1->positive = true;
			op2->positive = true;
			Assert::IsTrue(*op1 == *op2);
			Assert::IsFalse(*op1 < *op2);
			Assert::IsFalse(*op1 > *op2);

			// Positive, negative
			op1->positive = true;
			op2->positive = false;
			Assert::IsFalse(*op1 == *op2);
			Assert::IsFalse(*op1 < *op2);
			Assert::IsTrue(*op1 > *op2);

			// Negative, positive
			op1->positive = false;
			op2->positive = true;
			Assert::IsFalse(*op1 == *op2);
			Assert::IsTrue(*op1 < *op2);
			Assert::IsFalse(*op1 > *op2);

			// Negative, negative
			op1->positive = false;
			op2->positive = false;
			Assert::IsTrue(*op1 == *op2);
			Assert::IsFalse(*op1 < *op2);
			Assert::IsFalse(*op1 > *op2);
	
			// Change a high chunk (left < right)
			const int INDICES[2] = {1, op1->size-2};
			for (int i = 0; i < 2; i++) {
				if (op1->chunks[INDICES[i]] > 0) {
					op1->chunks[INDICES[i]] = op1->chunks[INDICES[i]]-1;
				} else {
					op1->chunks[INDICES[i]] = op1->chunks[INDICES[i]]+1;
				}

				// Positive, positive
				op1->positive = true;
				op2->positive = true;
				Assert::IsFalse(*op1 == *op2);
				Assert::IsTrue(*op1 < *op2);
				Assert::IsFalse(*op1 > *op2);

				// Negative, negative
				op1->positive = false;
				op2->positive = false;
				Assert::IsFalse(*op1 == *op2);
				Assert::IsFalse(*op1 < *op2);
				Assert::IsTrue(*op1 > *op2);

				// Change a high chunk (left > right)
				BigInt::chunkType swap = op1->chunks[INDICES[i]];
				op1->chunks[INDICES[i]] = op2->chunks[INDICES[i]];
				op2->chunks[INDICES[i]] = swap;

				// Positive, positive
				op1->positive = true;
				op2->positive = true;
				Assert::IsFalse(*op1 == *op2);
				Assert::IsFalse(*op1 < *op2);
				Assert::IsTrue(*op1 > *op2);

				// Negative, negative
				op1->positive = false;
				op2->positive = false;
				Assert::IsFalse(*op1 == *op2);
				Assert::IsTrue(*op1 < *op2);
				Assert::IsFalse(*op1 > *op2);

				// Restore equality
				op1->chunks[INDICES[i]] = op2->chunks[INDICES[i]];
			}
		}
	};
}
